package com.ccrfid.rmc6.service.device;

import com.ccrfid.rmc6.entity.device.CardDownLinkEntity;
import com.ccrfid.rmc6.entity.device.CardEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelView;
import com.ccrfid.rmc6.pojo.Constants;
import com.ccrfid.rmc6.pojo.amq.DownLinkRequest;
import com.ccrfid.rmc6.resource.device.pojo.MessageBody;
import com.ccrfid.rmc6.resource.device.pojo.RespData;
import com.ccrfid.rmc6.service.amq.MqMsgProducer;
import com.ccrfid.rmc6.service.personnel.PersonnelService;
import com.ccrfid.rmc6.util.DownLinkUtil;
import com.ccrfid.rmc6.util.JsonUtil;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Transactional
public class CardDownLinkServiceImpl implements CardDownLinkService{
    private static final Logger logger = LoggerFactory.getLogger(CardDownLinkServiceImpl.class);
    private BlockingQueue<CardDownLinkEntity> downLinkQueue;

    private Set<String> stopDownLinkSet = new CopyOnWriteArraySet<>();

    private Map<String,CardDownLinkEntity> cardDownLinkSuccessMap = new ConcurrentHashMap<>();

    //下发用户信息
    private final static int USER_INFO = 1;
    //校时
    private final static int TIMING = 2;

    //每一项最大字节数
    private final static int MAX_LENGTH = 8;

    @Value("${recv.buf.length}")
    private Integer lengthBase;
    @Value("${downlink.open}")
    private boolean downLinkOpen;
    @Value("${auto.downlink.m.timeout}")
    public Integer autoDownlinkTimeout;
    @Value("${manual.downlink.m.timeout}")
    public Integer manualDownlinkTimeout;
    @Value("${downlink.base.url}")
    public String engineUrl;
    @Value("${downlink.request.timeout}")
    public Integer timeout;
    @Value("${downlink.card.type}")
    public Integer cardType;
    private WebTarget webTarget;
    private static AtomicInteger seq = new AtomicInteger(0);

    @Autowired
    private PersonnelService personnelService;
    @Autowired
    private MqMsgProducer mqMsgProducer;

    @PostConstruct
    private void init(){
        if(downLinkOpen){
            logger.info("** init CoreLinkEngineProxyService ...");
            ClientConfig clientConfig = new ClientConfig();
            clientConfig.property(ClientProperties.CONNECT_TIMEOUT, timeout);
            clientConfig.property(ClientProperties.READ_TIMEOUT, timeout);
            this.webTarget = ClientBuilder.newClient(clientConfig).target(engineUrl);
            logger.info("** engineUrl:{}", webTarget.getUri());
            this.downLinkQueue = new LinkedBlockingDeque<>(1024 * (lengthBase > 8 ? lengthBase : 8));
            //下发卡片用户信息线程
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            if(downLinkQueue.size() > 0){
                                CardDownLinkEntity cardDownLinkEntity = downLinkQueue.take();
                                switch (cardDownLinkEntity.getDownLinkType()){
                                    case USER_INFO:
                                        if(!stopDownLinkSet.contains(cardDownLinkEntity.getTag())){
                                            if(cardDownLinkSuccessMap.get(cardDownLinkEntity.getTag()) == null ||
                                                    !Arrays.toString(cardDownLinkSuccessMap.get(cardDownLinkEntity.getTag()).getMsg()).equals(Arrays.toString(cardDownLinkEntity.getMsg()))){
                                                RespData respData = sendMessageGB18030(cardDownLinkEntity);
                                                if(respData.getCode() != 200 || !respData.getContent().equals(cardDownLinkEntity.getTag())){
                                                    repeatDownLinkInfo(cardDownLinkEntity.getCardNumber());
                                                    Thread.sleep(3000);
                                                }else {
                                                    cardDownLinkSuccessMap.put(cardDownLinkEntity.getTag(),cardDownLinkEntity);
                                                }
                                            }
                                        }else {
                                            stopDownLinkSet.remove(cardDownLinkEntity.getTag());
                                        }
                                        break;
                                    case TIMING:
                                        DownLinkRequest downLinkRequest = new DownLinkRequest(9, cardDownLinkEntity.getTag(),"",null);
                                        mqMsgProducer.send2Topic("corelink_downlink_request ", JsonUtil.INSTANCE.writeValueAsString(downLinkRequest));
                                        Thread.sleep(3000);
                                        break;
                                    default:
                                        break;
                                }


                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            thread.start();

        }
    }


    /**
     * 自动下发
     * @param
     */
    @Override
    public void downLinkInfo(CardEntity cardEntity) {
        if(downLinkOpen){
            PersonnelView personnelView = personnelService.getPersonnelDetailByCardNumber(cardEntity.getCardNumber());
            if(personnelView!= null){
                if(stopDownLinkSet.contains(personnelView.getHexNumber().toLowerCase())){
                    stopDownLinkSet.remove(personnelView.getHexNumber().toLowerCase());
                }
                downLinkTag(personnelView);
            }
        }
    }

    @Override
    public void stopDownLinkInfo(CardEntity cardEntity) {
        if(downLinkOpen){
            stopDownLinkSet.add(cardEntity.getHexNumber().toLowerCase());
        }
    }

    @Override
    public void timing(String hexNumber) {
        if(downLinkOpen){
            logger.info("timingTag:{}", hexNumber);
            CardDownLinkEntity cardDownLinkEntity = new CardDownLinkEntity();
            cardDownLinkEntity.setTag(hexNumber);
            cardDownLinkEntity.setDownLinkType(TIMING);
            downLinkQueue.offer(cardDownLinkEntity);
        }

    }

    /**
     * 手动下发
     * @param personId
     * @return
     */
    @Override
    public RespData downLinkInfo(Long personId) {
        RespData respData;
        if(downLinkOpen){
            PersonnelView personnelView = personnelService.getPersonnelDetailById(personId);
            if(personnelView.getHexNumber() != null){
                if(personnelView.getOnlineStatus() == Constants.ONLINE_STATUS.OFFLINE.id){
                    return new RespData(400,"下发信息失败，当前卡片处于离线状态");
                }else {
                    CardDownLinkEntity cardDownLinkEntity = new CardDownLinkEntity();
                    cardDownLinkEntity.setTagType(cardType);
                    cardDownLinkEntity.setDownLinkType(USER_INFO);
                    cardDownLinkEntity.setTag(personnelView.getHexNumber().toLowerCase());
                    cardDownLinkEntity.setCardNumber(personnelView.getCardNumber());
                    String[] msg ;
                    if(personnelView.getTitle()!= null && !personnelView.getTitle().equals("")){
                        msg = new String[]{
                                personnelView.getPersonnelName(),personnelView.getTitle(),personnelView.getPersonnelSn(),personnelView.getOrgName()};
                    }else {
                        msg = new String[]{
                                personnelView.getPersonnelName()," ",personnelView.getPersonnelSn(),personnelView.getOrgName()};
                    }

                    cardDownLinkEntity.setMsg(msg);
                    MessageBody body = new MessageBody(manualDownlinkTimeout);
                    Set<String> mac = new HashSet<>();
                    mac.add(cardDownLinkEntity.getTag());
                    body.setMacs(mac);
                    List<Integer> valueList = DownLinkUtil.getInitValue(0x51, getSeq(), cardDownLinkEntity.getTagType().intValue(),cardDownLinkEntity.getMsg().length);
                    for(int i = 0 ; i < cardDownLinkEntity.getMsg().length; i++){
                        byte[] data = cardDownLinkEntity.getMsg()[i].getBytes(Charset.forName("GB18030"));
                        if(data.length > MAX_LENGTH){
                            data = Arrays.copyOf(data, MAX_LENGTH);
                        }
                        List<Integer> itemList =  DownLinkUtil.getEncodeValue(data, "gb");
                        valueList.add(itemList.size());
                        valueList.addAll(itemList);
                    }
                    body.setValue(valueList);
                    RespData result = postMessage(body);
                    if(result.getCode() == 200 && result.getContent().equals(cardDownLinkEntity.getTag())){
                        respData = new RespData(200,"下发信息操作成功");
                    }else {
                        respData = new RespData(400,Constants.DOWN__ERROR_INFO.getResultInfo(result.getContent()));
                    }
                }
            }else {
               respData = new RespData(400,"该人员无对应卡片编号");
            }
        }else {
            respData = new RespData(400,"未开启下发用户信息操作");
        }
        return respData;
    }

    /**
     * 重复下发（重复下发重新获取卡片用户信息）
     * @param cardNumber
     */
    public void repeatDownLinkInfo(Long cardNumber) {
        if(downLinkOpen){
            PersonnelView personnelView = personnelService.getPersonnelDetailByCardNumber(cardNumber);
            if(personnelView != null){
                downLinkTag(personnelView);
            }
        }
    }

    private void downLinkTag(PersonnelView personnelView){
        CardDownLinkEntity cardDownLinkEntity = new CardDownLinkEntity();
        cardDownLinkEntity.setTagType(cardType);
        cardDownLinkEntity.setDownLinkType(USER_INFO);
        cardDownLinkEntity.setTag(personnelView.getHexNumber().toLowerCase());
        cardDownLinkEntity.setCardNumber(personnelView.getCardNumber());
        String[] msg ;
        if(personnelView.getTitle()!= null && !personnelView.getTitle().equals("")){
            msg = new String[]{
                    personnelView.getPersonnelName(),personnelView.getTitle(),personnelView.getPersonnelSn(),personnelView.getOrgName()};
        }else {
            msg = new String[]{
                    personnelView.getPersonnelName()," ",personnelView.getPersonnelSn(),personnelView.getOrgName()};
        }
        cardDownLinkEntity.setMsg(msg);
        downLinkQueue.offer(cardDownLinkEntity);
    }

    /****
     *下发卡片用户信息
     * @param cardDownLinkEntity
     * @return
     */
    public RespData sendMessageGB18030(CardDownLinkEntity cardDownLinkEntity) {
        //0x51
        MessageBody body = new MessageBody(autoDownlinkTimeout);
        Set<String> mac = new HashSet<>();
        mac.add(cardDownLinkEntity.getTag());
        body.setMacs(mac);
        List<Integer> valueList = DownLinkUtil.getInitValue(0x51, getSeq(), cardDownLinkEntity.getTagType().intValue(),cardDownLinkEntity.getMsg().length);
        for(int i = 0 ; i < cardDownLinkEntity.getMsg().length; i++){
            byte[] data = cardDownLinkEntity.getMsg()[i].getBytes(Charset.forName("GB18030"));
            if(data.length > MAX_LENGTH){
                data = Arrays.copyOf(data, MAX_LENGTH);
            }
            List<Integer> itemList =  DownLinkUtil.getEncodeValue(data, "gb");
            valueList.add(itemList.size());
            valueList.addAll(itemList);
        }
        body.setValue(valueList);
        return postMessage(body);
    }

    private RespData postMessage(MessageBody messageBody) {
        try {
            Entity entity  = Entity.entity(messageBody, MediaType.APPLICATION_JSON);
            Response response = webTarget
                    .path("beacons")
                    .path("message")
                    .request(MediaType.APPLICATION_JSON)
                    .post(entity);
            String content;
            Integer code = response.getStatus();
            if(code == 200){
                try{
                    content =  (String) response.readEntity(ArrayList.class).get(0);
                }catch (Exception e){
                    content = "";
                }
            }else {
                content = response.readEntity(String.class);
            }
            logger.info("downlink-card-info  response: {}, {}, request: {}", code, content, messageBody);
            return new RespData(code, content);
        } catch (Exception ex) {
            logger.error("ex: {}", ex.getMessage());
            return new RespData(444, ex.getMessage());
        }
    }

    private static int getSeq() {
        return seq.incrementAndGet();
    }
}
